Sections are what make up the largest part of the PE file except for all the preceding headers. Some sections have reserved names which describe their purpose. A full list can be found on Microsoft's documentation under "Special Sections".
.text
stores the programme's executable code..data
contains initialised data..bss
holds uninitialised data..rdata
contains read-only initialised data..edata
holds export tables..idata
stores import tables..reloc
has relocation information..rsrc
contains resources used by the program such as images or icons that are embedded..tls
is thread-local storage.The section header table lies between the Optional Header and the actual sections. Inside there is one Section Header entry for each section. Section headers are defined as follows in <winnt.h>
:
// Section header format.
#define IMAGE_SIZEOF_SHORT_NAME 8
typedef struct _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME];
union {
DWORD PhysicalAddress;
DWORD VirtualSize;
} Misc;
DWORD VirtualAddress;
DWORD SizeOfRawData;
DWORD PointerToRawData;
DWORD PointerToRelocations;
DWORD PointerToLinenumbers;
WORD NumberOfRelocations;
WORD NumberOfLinenumbers;
DWORD Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
Name
is a byte array which holds the section name. Due to its size, section names are limited to 8 characters in length, however, it is possible to circumvent this in non-executable PEs by using a string table.
Misc
- PhysicalAddress
or VirtualSize
, is a union field, meaning that it is either one or the other of its member fields. It represents the total size of the section when loaded into memory.
For executable images, VirtualAddress
holds the offset from the beginning of the image to the beginning of the section in memory. For object files it contains the address of the section before relocations are applied.
SizeOfRawData
stores the size of the section on disk and may be different from VirtualSize
. This field must a multiple of IMAGE_OPTIONAL_HEADER.FileAlignment
. If the section size is less than this value, then the section gets padded and this field is rounded to IMAGE_OPTIONAL_HEADER.FileAlignment
. However, upon loading into memory, the section no longer is required to obey the file alignment and so only its contents are loaded. Therefore, SizeOfRawData
will be less than VirtualSize
. It is possible for the opposite to happen as well. For example, no space will be allocated on disk for uninitialised data, however, the section will be expanded during load-time to reserve space for this data.
PointerToRawData
is a pointer to the first page of the section. For executables, it must be a multiple of IMAGE_OPTIONAL_HEADER.FileAlignment
.
PointerToRelocations
is a pointer to the beginning of the relocations for this section. For executables, this is set to 0.
PointerToLineNumbers
is pointer to the beginning of COFF line-number entries for the section. Since COFF debugging information is deprecated, this field holds 0.
NumberOfRelocations
stores the number of relocation entries for this section and is set to 0 for executable images.
NumberOfLinenumbers
is another deprecated field, which stores 0, and represents the number of COFF line-number entries for the section.
Characteristics
is a flags field which describes things like whether the section contains executable code, initialised/uninitialised data, etc.
The section headers can be inspected with PE-Bear:
Here, Raw Addr
and Virtual Addr
correspond to the IMAGE_SECTION_HEADER.PointerToRawData
and IMAGE_SECTION_HEADER.VirtualAddress
fields, respectively. Raw Size
and Virtual Size
correspond to IMAGE_SECTION_HEADER.SizeOfRawData
and IMAGE_SECTION_HEADER.VirtualSize
. The Characteristics
fields gives us information about whether the section is read-only, writable, executable, etc.
Data Directories represent pieces of data located within the sections of the PE file and contain information useful to the programme loader. They are simples structs defined in <winnt.h>
:
typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress;
DWORD Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
The first member, VirtualAddress
is an RVA pointing to the beginning of the Data Directory, while Size
holds the number of bytes that the Data Directory occupies. While this is true of all Data Directories, each Data Directory is handled differently based on its type:
// Directory Entries
#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory
#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 // Import Directory
#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 // Resource Directory
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 // Exception Directory
#define IMAGE_DIRECTORY_ENTRY_SECURITY 4 // Security Directory
#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 // Base Relocation Table
#define IMAGE_DIRECTORY_ENTRY_DEBUG 6 // Debug Directory
// IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 // (X86 usage)
#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 // Architecture Specific Data
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 // RVA of GP
#define IMAGE_DIRECTORY_ENTRY_TLS 9 // TLS Directory
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 // Load Configuration Directory
#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 // Bound Import Directory in headers
#define IMAGE_DIRECTORY_ENTRY_IAT 12 // Import Address Table
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 // Delay Load Import Descriptors
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 // COM Runtime descriptor
The above are values represent the indices for the Optional Header's DataDirectory
array at which each type of Data Directory is located.
If both the VirtualAddress
and Size
fields are set to 0, then this particular Data Directory is unused.
Data Directories can be inspected under the Optional Header with PE-Bear: